#include <QtGui/QPrintDialog>
#include <QtGui/QFileDialog>
#include <QtGui/QPushButton>
#include <QtGui/QVBoxLayout>
#include <QtGui/QMessageBox>
#include <QtCore/QFile>
#include <QtCore/QTextStream>
#include <Qsci/qsciprinter.h>
#include <QDebug>
#include "Editor.h"
#include "SettingsDialog.h"
#include "GoToLineWidget.h"
#include "FindReplaceWidget.h"
#include "Const.h"
#include "LexerCenter.h"
#include "AdvancedEditor.h"

AdvancedEditor::AdvancedEditor(QWidget *parent) : MultiEditor(parent) {
	qDebug() << "[AdvancedEditor] AdvancedEditor was created";

	// Messages Boxes creations
	
	saveOrNotDocDialog = new QMessageBox(parent);
	saveOrNotDocDialog->setWindowTitle(PROGRAM_NAME);
	saveOrNotDocDialog->setWindowIcon(QIcon(":/icons/main.png"));
	saveOrNotDocDialog->setIconPixmap(QPixmap(":/icons/main.png"));
	saveOrNotDocDialog->setText("<b>" + tr("Do you want to save the changes you made in the document?") 
								+ "</b><br>" + tr("Your changes will be lost, if you don't save them."));
	saveButtonDialog = saveOrNotDocDialog->addButton(tr("Save"),QMessageBox::ActionRole);
	dontSaveButtonDialog = saveOrNotDocDialog->addButton(tr("Don't save"),QMessageBox::ActionRole);
	cancelButtonDialog = saveOrNotDocDialog->addButton(tr("Cancel"),QMessageBox::ActionRole);
	
	// Other components creation
	
	printer = new QsciPrinter;
	printDialog = new QPrintDialog(printer,parent);
	
	settingsDialog = new SettingsDialog(parent);
	connect(settingsDialog,SIGNAL(settingsChanged()),this,SLOT(updateSettings()));
	
	lexerCenter = new LexerCenter;
	
	goToLineWidget = new GoToLineWidget;
	goToLineWidget->hide();
	connect(goToLineWidget,SIGNAL(goToLine(int)),this,SLOT(goToLineInCurrentEditor(int)));
	connect(goToLineWidget,SIGNAL(changeMaximum()),this,SLOT(changeMaximumOfGoToLine()));
	
	findReplaceWidget = new FindReplaceWidget;
	findReplaceWidget->hide();
	
	advancedEditorView = new QWidget(parent);
	QVBoxLayout *advancedEditorViewLayout = new QVBoxLayout;
	advancedEditorViewLayout->setContentsMargins(0,0,0,0);
	advancedEditorViewLayout->addWidget(getTabWidget(),1);
	advancedEditorViewLayout->addWidget(goToLineWidget);
	advancedEditorViewLayout->addWidget(findReplaceWidget);
	advancedEditorView->setLayout(advancedEditorViewLayout);
	
	showTabBarWhenOpenedOneEditor(false);
	
	connect(this,SIGNAL(currentTabCloseRequested()),this,SLOT(closeCurrentFile()));
	connect(this,SIGNAL(currentEditorChanged(Editor *)),findReplaceWidget,SLOT(setCurrentEditor(Editor *)));
	
	newFile();
}

AdvancedEditor::~AdvancedEditor() {
	delete saveButtonDialog;
	delete dontSaveButtonDialog;
	delete cancelButtonDialog;
	delete saveOrNotDocDialog;
	
	delete settingsDialog;
	delete lexerCenter;
	delete goToLineWidget;
	delete findReplaceWidget;
	
	delete printDialog;
}

void AdvancedEditor::newFile() {
	addEditor();
	openedFiles[getCurrentEditor()] = new FileInfo;
	emit (currentEditorChanged(getCurrentEditor()));
}

void AdvancedEditor::closeCurrentFile() {
	closeFile(getCurrentIndex());
}

void AdvancedEditor::closeFile(int index) {
	if (index < 0 || index >= count())
		return;
	Editor *editor = getEditor(index);
	FileInfo *fileInfo = getFileInfoOfEditor(editor);
	if (fileInfo == 0)
		return;
	if (editor->isModified()) {
		while (true) {
			saveOrNotDocDialog->exec();
			if (saveOrNotDocDialog->clickedButton() == saveButtonDialog) {
				if (saveFile(index) == -1)
					continue;
			} else if (saveOrNotDocDialog->clickedButton() == cancelButtonDialog)
				return;
			break;
		}
	}
	qDebug() << "[AdvancedEditor] Closed file with path" << fileInfo->filePath;
	
	openedFiles.remove(editor);
	closeEditor(index);
	emit (closedFileWithPath(fileInfo->filePath));
}

int AdvancedEditor::openFile(QString filePath) {
	qDebug() << "[AdvancedEditor] Openning file with path" << filePath;

	// TODO: relatives pathes such as ./ or name
	
	QFile fileToOpen(filePath);
	if (fileToOpen.open(QIODevice::ReadOnly)) {
		bool openFileInCurrentTab = false;
		
		if (getCurrentEditor() != 0)
			if (getFileInfoOfEditor(getCurrentEditor())->filePath.isEmpty() && !getCurrentEditor()->isModified())
				openFileInCurrentTab = true;
		
		FileInfo *fileInfo = openFileInCurrentTab ? getFileInfoOfEditor(getCurrentEditor()) : new FileInfo;
		
		fileInfo->filePath = filePath;
		fileInfo->fileExtenssion = QFileInfo(filePath).suffix();
		fileInfo->fileName = QFileInfo(filePath).fileName();
		fileInfo->extenssionForSyntax = QFileInfo(filePath).suffix();
			
		if (!openFileInCurrentTab) {
			addEditor(fileInfo->fileName);
			openedFiles[getCurrentEditor()] = fileInfo;
		} else
			setCurrentTabText(fileInfo->fileName);
	
		QTextStream stream(&fileToOpen);
		stream.setCodec(fileInfo->fileEncoding.toAscii());
		getCurrentEditor()->setText(stream.readAll());	
		
		setSyntaxForExtenssion(fileInfo->extenssionForSyntax);
		
		setCurrentEditorModification(false);
		
		emit (currentEditorChanged(getCurrentEditor()));
		emit(openedFileWithPath(filePath));
	} else
		return fileToOpen.error();
	return 0;
}

int AdvancedEditor::openFiles(const QStringList& pathes) {
	QStringList filesPathes;
	if (pathes.isEmpty()) {
		FileInfo* fileInfo = getFileInfoOfEditor(getCurrentEditor());
		QString startDirPath;
		if (fileInfo != 0)
			startDirPath = fileInfo->filePath.isEmpty() ? QDir::homePath() : QFileInfo(fileInfo->filePath).absolutePath();
		else
			startDirPath = QDir::homePath();
		filesPathes = QFileDialog::getOpenFileNames(qobject_cast<QWidget *>(advancedEditorView->parent()),tr("Open files"),startDirPath);
		if (filesPathes.isEmpty())
			return 0;
	} else
		filesPathes = pathes;
	
	foreach (QString filePath,filesPathes) {
		
		// May be file with filePath was already opened
		bool alreadyOpened = false;
		foreach (FileInfo *openedFile,openedFiles) {
			if (openedFile == 0 || openedFile->filePath.isEmpty())
				continue;
			if (openedFile->filePath == filePath) {
				moveToEditorWithName(openedFile->fileName);
				alreadyOpened = true;
				break;
			}
		}
		if (alreadyOpened)
			continue;
		
		int openStatus = openFile(filePath);
		if (openStatus != 0)
			return openStatus;
	}
	return 0;
}

int AdvancedEditor::saveFile(int index) {
	if (index < 0 || index >= count())
		return 0;
	Editor *editor = getEditor(index);
	FileInfo* fileInfo = getFileInfoOfEditor(editor);
	
	if (fileInfo->filePath.isEmpty())
		return saveFileAs(index);
	else {
		QFile fileToSave(fileInfo->filePath);
		if (fileToSave.open(QIODevice::WriteOnly)) {
			QTextStream stream(&fileToSave);
			stream.setCodec(fileInfo->fileEncoding.toAscii());
			stream << editor->text();
			
			setTabText(index,fileInfo->fileName);
			setEditorModification(index,false);
			emit(openedFileWithPath(fileInfo->filePath));
		} else
			return fileToSave.error();
	}
	qDebug() << "[AdvancedEditor] Saving file with file path" << fileInfo->filePath;
	return 0;
}

int AdvancedEditor::saveCurrentFile() {
	return saveFile(getCurrentIndex());
}

int AdvancedEditor::saveFileAs(int index) {
	if (index < 0 || index >= count())
		return 0;
	Editor *editor = getEditor(index);
	FileInfo* fileInfo = getFileInfoOfEditor(editor);
	
	QString startDirPath = fileInfo->filePath.isEmpty() ? QDir::homePath() : QFileInfo(fileInfo->filePath).absolutePath();
	QString filePath = QFileDialog::getSaveFileName(qobject_cast<QWidget *>(advancedEditorView->parent()),tr("Save file as"),startDirPath);
	
	if (filePath.isEmpty())
		return -1;
	fileInfo->filePath = filePath;
	fileInfo->fileExtenssion = QFileInfo(filePath).suffix();
	fileInfo->fileName = QFileInfo(filePath).fileName();
	return saveFile(index);	

}

int AdvancedEditor::saveCurrentFileAs() {
	return saveFileAs(getCurrentIndex());
}

int AdvancedEditor::reloadFile() {
	if (getCurrentEditor() == 0)
		return 0;
	
	FileInfo* fileInfo = getFileInfoOfEditor(getCurrentEditor());
	if (!fileInfo->filePath.isEmpty()) {
		QFile fileToReload(fileInfo->filePath);
		if (fileToReload.open(QIODevice::ReadOnly)) {
			QTextStream stream(&fileToReload);
			stream.setCodec(fileInfo->fileEncoding.toAscii());
			getCurrentEditor()->setText(stream.readAll());
			
			setCurrentEditorModification(false);
		} else
			return fileToReload.error();
	}
	return 0;
}

void AdvancedEditor::printFile() {
	if (getCurrentEditor() == 0)
		return;
	
	if (printDialog->exec() == QDialog::Accepted) {
		printer->setWrapMode(getCurrentEditor()->wrapMode());
		printer->printRange(getCurrentEditor(), 0);
	}
}

void AdvancedEditor::showSettings() {
	settingsDialog->show();
}

void AdvancedEditor::showGoToLineWidget() {
	goToLineWidget->showWidget();
	goToLineWidget->setMaximum(getLinesCountOfCurrentEditor());
}

void AdvancedEditor::showFindReplaceWidget() {
	findReplaceWidget->show();
}

QWidget *AdvancedEditor::getAdvancedEditorView() const {
	return advancedEditorView;
}

void AdvancedEditor::updateSettings() {
	foreach (Editor *editor,getOpenedEditors())
		editor->updateSettings();
}

void AdvancedEditor::setEncoding(const QString& encodingName) {
	if (getCurrentEditor() == 0)
		return;
	
	FileInfo *fileInfo = getFileInfoOfEditor(getCurrentEditor());
	if (fileInfo == 0)
		return;
	
	fileInfo->fileEncoding = encodingName;
	reloadFile();
}

void AdvancedEditor::setSyntaxForExtenssion(const QString& extenssion) {
	if (getCurrentEditor() == 0)
		return;
	
	FileInfo *fileInfo = getFileInfoOfEditor(getCurrentEditor());
	if (fileInfo == 0)
		return;
	
	fileInfo->extenssionForSyntax = extenssion;
	getCurrentEditor()->setLexer(lexerCenter->lexerForExtenssion(extenssion));
}

FileInfo *AdvancedEditor::getFileInfoOfEditor(Editor *editor) const {
	if (editor == 0)
		return 0;
	
	if (openedFiles.contains(editor))
		return openedFiles[editor];
	return 0;
}

QStringList AdvancedEditor::getAllOpenedFilePathes() {
	QStringList list;
	foreach (FileInfo *fileInfo,openedFiles)
		list << fileInfo->filePath;
	return list;
}

void AdvancedEditor::clear() {
	openFirstTab();
	for (int i = count() - 1; i >= 0; i--)
		closeFile(i);
}

void AdvancedEditor::changeMaximumOfGoToLine() {
	goToLineWidget->setMaximum(getLinesCountOfCurrentEditor());
}
